package maxflow;

import ilog.concert.IloLinearNumExpr;
import ilog.concert.IloNumVar;
import ilog.cplex.IloCplex;

import java.math.BigDecimal;
import java.util.List;

import support.AlgorithmSupport;
import util.NullOutputStream;
import cinvestment.Plant;

public class MaxFlow {

	public MaxFlow(int productNum, int simulationCount, String str) {
		this.product_num = productNum;
		this.structure = str;
		this.simulationCount = simulationCount;
	}

	public void setVariance(double var) {
		this.demandVariance = var;
	}

	private int simulationCount;
	private int product_num;// product numbers
	private int plant_num;// plant numbers
	private List<Plant> plants;
	private double[][] demands;
	private double[] capcities;
	private double demandVariance = 0.3;

	private String structure;

	public void setCapacities(double[] caps) {
		this.capcities = caps;
	}

	private void init() {
		this.plants = AlgorithmSupport.createPlantsByStructure(product_num, structure);
		this.plant_num = plants.size();
		this.demands = AlgorithmSupport.getDemands_normal(simulationCount, product_num,
				1.0, this.demandVariance, 0, 2.0);
		// this.demands = AlgorithmSupport.getDemands_uniform(simulationCount,
		// N,
		// 0.0, 2.0);

		if (this.capcities == null || this.capcities.length == 0) {
			double cap = BigDecimal
					.valueOf(product_num)
					.divide(BigDecimal.valueOf(plant_num), 5,
							BigDecimal.ROUND_HALF_EVEN).doubleValue();
			this.capcities = new double[plant_num];
			for (int j = 0; j < plant_num; j++)
				capcities[j] = cap;
		}
	}

	public double getExpectedMaxFlow() {
		this.init();
		BigDecimal expected = BigDecimal.ZERO;
		for (int m = 0; m < demands.length; m++) {
			try {
				IloCplex cplex = new IloCplex();
				cplex.setOut(new NullOutputStream());

				IloNumVar[][] x = new IloNumVar[product_num][plant_num];
				for (int i = 0; i < product_num; i++)
					for (int j = 0; j < plant_num; j++)
						x[i][j] = cplex.numVar(0.0, Double.MAX_VALUE);

				IloLinearNumExpr obj = cplex.linearNumExpr();
				for (int i = 0; i < product_num; i++) {
					for (int j = 0; j < plant_num; j++) {
						if (plants.get(j).isAvailable(i))
							obj.addTerm(1.0, x[i][j]);
					}
				}
				cplex.addMaximize(obj);

				for (int i = 0; i < product_num; i++) {
					IloLinearNumExpr pro = cplex.linearNumExpr();
					for (int j = 0; j < plant_num; j++) {
						if (plants.get(j).isAvailable(i))
							pro.addTerm(1.0, x[i][j]);
					}
					cplex.addLe(pro, demands[m][i]);
				}
				for (int j = 0; j < plant_num; j++) {
					IloLinearNumExpr cap = cplex.linearNumExpr();
					for (int i = 0; i < product_num; i++)
						if (plants.get(j).isAvailable(i))
							cap.addTerm(1.0, x[i][j]);
					cplex.addLe(cap, capcities[j]);
				}
				if (cplex.solve()) {
					double objv = cplex.getObjValue();
					expected = expected.add(BigDecimal.valueOf(objv));
				}
				cplex.end();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		expected = expected.divide(BigDecimal.valueOf(demands.length), 5,
				BigDecimal.ROUND_HALF_UP);

		return expected.doubleValue();
	}

}
